﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Drawing;
using System.ComponentModel;
using BastInfos.Helpers;
using System.Web.Mvc;

namespace BastInfos.Controls
{
	public class Calendar
	{
		private Dictionary<int, DayLink> links;
		private int _month = DateTime.Now.Month;
		private int _year = DateTime.Now.Year;
		private DateTime? _selectedDate = null;
		private bool _showMonth = false;
		private bool _showYear = false;
		private string _headerText = null;
		private string _css = null;

		public Calendar()
		{
			links = new Dictionary<int, DayLink>();
		}

		public Calendar(int month, int year)
			: this()
		{
			_month = month;
			_year = year;
		}

		public Calendar HeaderText(string headerText)
		{
			_headerText = headerText;
			return this;
		}

		public Calendar ShowMonth(bool showMonth)
		{
			_showMonth = showMonth;
			return this;
		}

		public Calendar ShowYear(bool showYear)
		{
			_showYear = showYear;
			return this;
		}

		public Calendar Css(string css)
		{
			_css = css;
			return this;
		}

		public Calendar SelectedDate(DateTime selectedDate)
		{
			_selectedDate = selectedDate;
			_month = _selectedDate.Value.Month;
			_year = _selectedDate.Value.Year;
			return this;
		}

		public Calendar AddLink(int day, string url, Color color, object htmlAttributes = null)
		{
			if (links.ContainsKey(day))
				links[day] = new DayLink(url, color, htmlAttributes);
			else
				links.Add(day, new DayLink(url, color, htmlAttributes));

			return this;
		}

		public MvcHtmlString Render()
		{
			StringBuilder builder = new StringBuilder();
			DateTime date = new DateTime(_year, _month, 1);

			RenderHeader(builder, date);

			int startDay = ToMondayStart((int)date.DayOfWeek);
			int i = 0;

			for (i = 0; i < startDay; i++)
				builder.Append("<td class=\"empty\">&nbsp;</td>");

			for (i = 1; i <= DateTime.DaysInMonth(_year, _month); i++)
			{
				if ((i + startDay) % 7 == 1)
					builder.Append("</tr><tr>");

				if (links.ContainsKey(i))
				{
					if (links[i].Color != Color.Empty)
						builder.AppendFormat("<td style=\"background-color:#{3};\"><a href=\"{1}\" {2}>{0}</a></td>", i, links[i].Url, links[i].HtmlAttributesToString(), links[i].Color.ToArgb());
					else
						builder.AppendFormat("<td><a href=\"{1}\" {2}>{0}</a></td>", i, links[i].Url, links[i].HtmlAttributesToString());
				}
				else if (_selectedDate.HasValue && _selectedDate.Value.Day == i)
				{
					builder.AppendFormat("<td class=\"selected\">{0}</td>", i);
				}
				else
				{
					builder.AppendFormat("<td>{0}</td>", i);
				}
			}

			if (i < 35)
			{
				while (i < 35)
					builder.AppendFormat("<td class=\"empty\">&nbsp;</td>", i++);
			}
			else if (i < 42)
			{
				while (i < 42)
					builder.AppendFormat("<td class=\"empty\">&nbsp;</td>", i++);
			}

			RenderFooter(builder, date);
			return MvcHtmlString.Create(builder.ToString());
		}

		private void RenderHeader(StringBuilder builder, DateTime date)
		{
			if (!_css.IsNullOrEmpty())
				builder.AppendFormat("<table class=\"{0}\">", _css);
			else
				builder.Append("<table>");

			if (!_headerText.IsNullOrEmpty())
				builder.AppendFormat("<tr><th colspan=\"7\" class=\"calendar_header\">{0}</th></tr>", _headerText);

			if (_showMonth || _showYear)
				builder.AppendFormat("<tr><th colspan=\"7\" class=\"calendar_date\">");
			if (_showMonth)
				builder.Append(String.Format("{0:MMMM}", date).Substring(0, 1).ToUpper() + String.Format("{0:MMMM}", date).Substring(1));
			if (_showMonth && _showYear)
				builder.Append(" - ");
			if (_showMonth)
				builder.AppendFormat("{0:yyyy}", date);
			if (_showMonth || _showYear)
				builder.AppendFormat("</th></tr>");

			RenderDaysName(builder);
		}

		private void RenderDaysName(StringBuilder builder)
		{
			for (int i = 0; i < 7; i++)
			{
				string dayName = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.GetAbbreviatedDayName((DayOfWeek)(i + 1 == 7 ? 0 : i + 1));
				dayName = dayName.Substring(0, 1).ToUpper() + dayName.Replace(".", String.Empty).Substring(1);
				builder.AppendFormat("<td class=\"day\">{0}</td>", dayName);
			}
			builder.AppendFormat("</th></tr>");
		}

		private void RenderFooter(StringBuilder builder, DateTime date)
		{
			builder.Append("</tr></table>");
		}

		private int ToMondayStart(int dayOfWeek)
		{
			if (dayOfWeek == 0)
				dayOfWeek = 7;
			return --dayOfWeek;
		}

		private class DayLink
		{
			internal string Url { get; private set; }
			internal Color Color { get; private set; }
			internal object HtmlAttributes { get; private set; }

			internal DayLink(string url, Color color, object htmlAttributes)
			{
				Url = url;
				Color = color;
				HtmlAttributes = htmlAttributes;
			}

			internal string HtmlAttributesToString()
			{
				StringBuilder builder = new StringBuilder();
				PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(HtmlAttributes);
				foreach (PropertyDescriptor property in properties)
					builder.AppendFormat("{0}=\"{1}\" ", property.Name, property.GetValue(HtmlAttributes));

				return builder.ToString();
			}
		}
	}
}